Amazon ECS対応のスケジューラBloxを試してみた #reinvent
ども、大瀧です。
先週開催されたAWSの年次イベントre:Inventで発表された、OSSのコンテナスケジューラであるBloxを試してみた様子をレポートします。
Bloxとは
Blox公式ページの説明を引用します。
Blox is a collection of open source projects for container management and orchestration on Amazon ECS
"Amazon ECS向けのコンテナ管理&オーケストレーションツール群"という訳語が適当でしょうか。Amazon ECSには元々、クラスタ管理や簡易なコンテナスケジューラが組み込まれていますが、コンテナ配置についてはおおまかな設定ができる"Service"と、詳細に指定する"Task"の2種類で、その中間の位置づけとなるようないわゆるコンテナスケジューラはユーザーで用意してね、というスタンスでした。Bloxはこの領域をカバーするOSSとして公開され、現在はAWSが主体となって開発が進められています。
Bloxのデプロイ方法
現時点で用意されている、Bloxの実行方法は以下の2通りです。
- ローカルのDocker環境
- AWS環境
今回は比較的手軽に試すことができる、ローカルのDocker環境で試してみました。ローカルのDocker環境で実行する場合のアーキテクチャを以下に示します。
ローカルとは言いつつ、AWSアカウントといくつかの最低限のサービスが構成として必要です。詳細は手順で後述します。Docker側は3つのコンテナが動作し、Bloxとしては以下2つのコンポーネントになります。
- Blox - Daemon Scheduler : 現在Bloxで実装されている唯一のコンテナスケジューラ
- Blox - Cluster State Service(以下CSS) : スケジューラがECSクラスタの状態を把握するために利用するバックエンドサービス
- etcd : Daemon SchedulerとCSSがデータストアとして利用するKVS
デプロイ手順
動作確認環境
- OS : macOS Sierra バージョン 10.12.1
- Docker : Docker for Mac バージョン 1.12.3
- AWSリージョン : 東京
1. ECSクラスタの作成
Bloxの管理対象となるECSクラスタを作成します。クラスタの制約は特にないので、既存のクラスタがあればそれを利用しても問題ありません。試しに新規で作成するのであれば、ECSのウィザードで作成するのが良いでしょう。今回はウィザードからクラスタ名default、t2.micro 1台で作成しました。
2. CloudFormationの実行
続いてCSSがECSクラスタのイベントを取得する仕組みとして、最近ECSのイベントがソースとしてサポートされたCloudWatch Eventsとそのイベントの保存先としてのSQSを、CloudFormationで作成します。CloudFormationテンプレートはGitHubのBloxリポジトリ(https://github.com/blox/blox/)にあるので、git clone
しCloudFormation実行時にテンプレートファイルを指定します。
$ git clone https://github.com/blox/blox.git $ cd blox $ aws cloudformation create-stack --stack-name BloxLocal --template-body file://./deploy/docker/conf/cloudformation_template.json
しばらく待つと、CloudFormationスタック(今回はスタック名BloxLocal)の作成が完了します。CloudFormationの管理画面から作成されたリソース一覧が確認できます。
AWS側の準備はこれでOKです。
3. Docker Composeの実行
Dockerの3つのコンテナは、Docker for MacにバンドルされるDocker Composeでまとめて管理します。Docker Composeの構成ファイルであるdocker-compose.yml
はdeploy/docker/conf/
ディレクトリにあり一部設定しなければならない項目があるので、あらかじめ確認しておきましょう。
$ ls CHANGELOG.md Godeps/ README.md daemon-scheduler/ licenses/ CONTRIBUTING.md LICENSE cluster-state-service/ deploy/ vendor/ $ cd deploy/docker/conf/ $ ls cloudformation_policy.json cloudformation_template.json docker-compose.yml $
docker-compose.yml
では、各コンテナがAWSにアクセスするための設定が2箇所に2つずつ(AWSリージョン名: AWS_REGION
とIAMユーザー名": AWS_PROFILE
)あり、実行する環境にあわせて変更する必要があります。今回は東京リージョンと、あらかじめ作成したIAMユーザー名blox
を設定しました。その他はそのままでOKです。
version: '2' services: scheduler: image: "bloxoss/daemon-scheduler:0.1.0" ports: - "2000:2000" environment: AWS_REGION: "ap-northeast-1" AWS_PROFILE: "blox" command: [ "--bind", "0.0.0.0:2000", "--css-endpoint", "css:3000", "--etcd-endpoint", "etcd:2379" ] links: - "css:css" - "etcd:etcd" volumes: - "~/.aws:/.aws:ro" depends_on: - "css" - "etcd" css: image: "bloxoss/cluster-state-service:0.1.0" ports: - "3000:3000" environment: AWS_REGION: "ap-northeast-1" AWS_PROFILE: "blox" command: [ "--bind", "0.0.0.0:3000", "--etcd-endpoint", "etcd:2379", "--queue", "blox_queue" ] links: - "etcd:etcd" volumes: - "~/.aws:/.aws:ro" depends_on: - "etcd" etcd: image: "quay.io/coreos/etcd:v3.0.13" ports: - "2379:2379" command: [ "/usr/local/bin/etcd", "--data-dir", "/var/lib/etcd/data", "--wal-dir", "/var/lib/etcd/wal", "--listen-client-urls", "http://0.0.0.0:2379", "--advertise-client-urls", "http://0.0.0.0:2379", "--listen-peer-urls", "http://0.0.0.0:2380" ] volumes: - "~/blox-state:/var/lib/etcd"
schedulerとcssのvolumes
を見るとわかりますが、コンテナからはMac側のAWSクレデンシャルファイルをマウントして上記のプロファイル名に対応するAPIキーを取得する形になります。AWSのAPI認証でエラーになる場合はこのあたりを見直しましょう。
では、Docker Composerを実行しコンテナを起動します。
$ docker-compose up -d Starting conf_etcd_1 Starting conf_css_1 Starting conf_scheduler_1 $ docker-compose ps Name Command State Ports -------------------------------------------------------------------------------------------- conf_css_1 /cluster-state-service --b ... Up 0.0.0.0:3000->3000/tcp, 80/tcp conf_etcd_1 /usr/local/bin/etcd --data ... Up 0.0.0.0:2379->2379/tcp, 2380/tcp conf_scheduler_1 /daemon-scheduler --bind 0 ... Up 0.0.0.0:2000->2000/tcp $
起動しました!コンテナが終了してしまう場合は、docker logs <コンテナ名>
などでトラブルシュートしましょう。私の環境ではcssが起動しないトラブルに遭ったので、以下の記事にまとめておきました。参考になれば幸いです。
これで準備OKです。
動作確認
それでは、CSSとDaemon Schedulerそれぞれの動作を確認してみます。CSSはECSクラスタのイベントをトラックしてクラスタの状態情報を保持し、localhostのポート番号3000でREST APIをサービスします。API一覧はGitHubのswagger.json
で確認できます。試しにECSクラスタのインスタンス一覧をコールしてみましょう。
$ curl localhost:3000/v1/instances {"items":[{"EC2InstanceID":"i-18d80d86","agentConnected":true,"attributes":null,"clusterARN":"arn:aws:ecs:ap-northeast-1:XXXXXXXXXXXX:cluster/default","containerInstanceARN":"arn:aws:ecs:ap-northeast-1:XXXXXXXXXXXX:container-instance/42c9130c-2eb8-4b53-970d-b73ca0bf2f4c","registeredResources":[{"name":"CPU","type":"INTEGER","value":null},{"name":"MEMORY","type":"INTEGER","value":null},{"name":"PORTS","type":"STRINGSET","value":null},{"name":"PORTS_UDP","type":"STRINGSET","value":null}],"remainingResources":[{"name":"CPU","type":"INTEGER","value":null},{"name":"MEMORY","type":"INTEGER","value":null},{"name":"PORTS","type":"STRINGSET","value":null},{"name":"PORTS_UDP","type":"STRINGSET","value":null}],"status":"ACTIVE","versionInfo":{"agentHash":"efe53c6","agentVersion":"1.13.1","dockerVersion":"DockerVersion: 1.11.2"}}]} $
取得できる情報自体はECSのAPIと同等ですので、CSSはあくまでBloxスケジューラからECSを参照するためのAPIラッパーと考えるのが良さそうです。
続いて、Daemon Schedulerです。Daemon Schedulerは見本のスケジューラとして提示されるもので、独自のスケジューラを実装するためのひな形として利用できそうです。また、今後新たなスケジューラを追加する予定とのことです(GitHubのREADMEの記述を引用します)。
The scheduler can be used as a reference for how to use the cluster-state-service to build custom scheduling logic, and we plan to add additional scheduling capabilities for different use cases.
Daemon Schedulerは1インスタンス毎に1つのタスクを実行するシンプルなスケジューラです。ECSクラスタのインスタンス追加・削除に追随するようになっています。スケジューラ自体の設定は、localhostのポート番号2000でListenするREST APIを利用します。API一覧はGitHubのswagger.json
で参照できます。まずはGET /v1/ping
で200レスポンスが返ってくるか、動作を確認しましょう。
$ curl -v localhost:2000/v1/ping * Trying ::1... * Connected to localhost (::1) port 2000 (#0) > GET /v1/ping HTTP/1.1 > Host: localhost:2000 > User-Agent: curl/7.49.1 > Accept: */* > < HTTP/1.1 200 OK < Content-Type: application/json; charset=UTF-8 < Date: Wed, 07 Dec 2016 00:11:19 GMT < Content-Length: 0 < * Connection #0 to host localhost left intact
Daemon Schuedulerの動作を簡単に確認できるデモスクリプトがリポジトリのdeploy/demo-cli/
にあるので、今回はこちらを利用します。
$ cd ../../demo-cli/ suzaku:demo-cli ryuta$ ls README.md blox-list-environments.py* css-list-tasks.py* task-definition.json blox-create-deployment.py* common.py increment-cluster-instances.py* blox-create-environment.py* common.pyc list-task-definitions.py* blox-list-deployments.py* css-list-instances.py* register-task-definition.py* $
Daemon Schedulerでは、タスクと実行するクラスタの組み合わせEnvironmentとして定義します。今回はディレクトリにあるサンプルファイルtask-definition.json
からタスク定義を作成し、Environment作成時に指定してみます。
$ aws ecs register-task-definition --cli-input-json file://task-definition.json { "taskDefinition": { "status": "ACTIVE", "family": "sleep300", "volumes": [], "taskDefinitionArn": "arn:aws:ecs:ap-northeast-1:XXXXXXXXXXXX:task-definition/sleep300:2", "containerDefinitions": [ :(中略) ], "revision": 2 } } $ ./blox-create-environment.py \ --environment TestEnvironment \ --cluster default \ --task-definition arn:aws:ecs:ap-northeast-1:XXXXXXXXXXXX:task-definition/sleep300:2 == Blox Demo CLI - Create Blox Environment == HTTP Response Code: 200 { "deploymentToken": "e3cf6414-47fb-4bbc-be15-ca834c7db6a0", "health": "healthy", "name": "TestEnvironment", "instanceGroup": { "cluster": "arn:aws:ecs:ap-northeast-1:XXXXXXXXXXXX:cluster/default" } } $
オプションで指定したEnvironment名(TestEnvironment
)とレスポンスに含まれるデプロイメントトークンをデプロイ時に指定するので、覚えておきましょう。
では、スケジュール設定をデプロイ(有効化)します。
$ ./blox-create-deployment.py \ --environment TestEnvironment \ --deployment-token e3cf6414-47fb-4bbc-be15-ca834c7db6a0 == Blox Demo CLI - Create Blox Deployment == HTTP Response Code: 200 { "status": "pending", "environmentName": "TestEnvironment", "id": "9071d07b-a7ba-4930-b45c-cbf23f9ec4cf", "failedInstances": [], "taskDefinition": "arn:aws:ecs:ap-northeast-1:XXXXXXXXXXXX:task-definition/sleep300:2" }
これで、Daemon SchedulerはEnvironmentの定義に従い、インスタンス毎のタスク実行を管理するようになりました。ECSの実行タスク一覧を確認すると、定義したsleep300タスクが実行状態であることが確認できました!
では、クラスタにインスタンスを追加してタスクの様子を見てみましょう。ウィザードで作成するECSクラスタのインスタンスはAuto Scalingで構成されるので、EC2の管理画面からAuto ScalingグループEC2ContainerService-default-EcsInstanceAsg-<ランダム文字列>
のインスタンス数を1→2に変更します。
しばらく待つとクラスタに追加したインスタンスが起動し、その追加イベントをDaemon Schedulerが捕捉、タスクが実行されるはずです。ECSタスク一覧を更新すると。。。
タスクが増えていますね。きちんとスケジューラが動作していることがわかります。
ちなみにこのsleep300タスクは名前の通り300秒(5分)sleep
コマンドを実行して終了するタスクなので、デプロイから5分経つとタスクは一旦終了します。一方Daemon Schedulerはインスタンス毎にタスク実行を維持する機能があるため、終了を検知すると新たなタスクを実行するようにもなっています。停止タスク一覧でその様子が確認できます。
まとめ
Bloxの基本的な機能と動作を一通り解説してみました。現時点では特別なにかができると言ったものはないですが、ユーザーが独自にスケジューラを開発して組み込める点、今後コミュニティドリブンで新たなスケジューラが追加される点に期待していいのではないでしょうか。
また、当然スケジューラが停止するとタスク管理も維持できませんので、高可用性を備えたAWS環境での構成も試してみたいところですね。